home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mush-7.1.1 / bind.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-02  |  20.1 KB  |  658 lines

  1. /* bind.c */
  2.  
  3. #include "bindings.h"
  4. #include "mush.h"
  5.  
  6. extern char *c_macro();
  7. static un_bind();
  8.  
  9. struct cmd_map *cmd_map, *line_map, *bang_map;
  10.  
  11. /*
  12.  * Bindings are added here in REVERSE of the order that
  13.  * they will be displayed!  Display order is based on a
  14.  * guess about the frequency of use and (to a lesser
  15.  * extent) how hard they are to remember.
  16.  *
  17.  * The user's own new bindings, if any, will be displayed
  18.  * before any of these default bindings.
  19.  */
  20. init_bindings()
  21. {
  22. #ifdef CURSES
  23.     /* Help gets displayed last */
  24.     add_bind("?", C_HELP, NULL, &cmd_map);
  25.     add_bind("V", C_VERSION, NULL, &cmd_map);
  26.  
  27.     /* Miscellaneous shell commands */
  28.     add_bind("%", C_CHDIR, NULL, &cmd_map);
  29.     add_bind("|", C_PRINT_MSG, NULL, &cmd_map);
  30.     add_bind("!", C_SHELL_ESC, NULL, &cmd_map);
  31.     add_bind(":", C_CURSES_ESC, NULL, &cmd_map);
  32.  
  33.     /* Mush customization commands */
  34.     /* NOTE: No default C_MACRO bindings */
  35.     add_bind(")", C_SAVEOPTS, NULL, &cmd_map);
  36.     add_bind("(", C_SOURCE, NULL, &cmd_map);
  37.     add_bind("&!", C_MAP_BANG, NULL, &cmd_map);
  38.     add_bind("&:", C_MAP, NULL, &cmd_map);
  39.     add_bind("&&", C_BIND_MACRO, NULL, &cmd_map);
  40.     add_bind("v", C_VAR_SET, NULL, &cmd_map);
  41.     add_bind("i", C_IGNORE, NULL, &cmd_map);
  42.     add_bind("h", C_OWN_HDR, NULL, &cmd_map);
  43.     add_bind("B", C_UNBIND, NULL, &cmd_map);
  44.     add_bind("b", C_BIND, NULL, &cmd_map);
  45.     add_bind("a", C_ALIAS, NULL, &cmd_map);
  46.  
  47.     /* Display modification commands */
  48.     add_bind("\022", C_REVERSE, NULL, &cmd_map);    /* ^R */
  49.     add_bind("\014", C_REDRAW, NULL, &cmd_map);        /* ^L */
  50.     add_bind("Z", C_PREV_SCREEN, NULL, &cmd_map);
  51.     add_bind("z", C_NEXT_SCREEN, NULL, &cmd_map);
  52.  
  53.     /* Searching and sorting commands */
  54.     add_bind("\016", C_CONT_SEARCH, NULL, &cmd_map);    /* ^N */
  55.     add_bind("\037", C_PREV_SEARCH, NULL, &cmd_map);    /* ^/ */
  56.     add_bind("/", C_NEXT_SEARCH, NULL, &cmd_map);
  57.     add_bind("O", C_REV_SORT, NULL, &cmd_map);
  58.     add_bind("o", C_SORT, NULL, &cmd_map);
  59.  
  60.     /* Ways to get out */
  61.     add_bind("X", C_EXIT_HARD, NULL, &cmd_map);
  62.     add_bind("x", C_EXIT, NULL, &cmd_map);
  63.     add_bind("Q", C_QUIT_HARD, NULL, &cmd_map);
  64.     add_bind("q", C_QUIT, NULL, &cmd_map);
  65.  
  66.     /* Folder modification commands */
  67.     add_bind("\025", C_UPDATE, NULL, &cmd_map);        /* ^U */
  68.     add_bind("\020", C_PRESERVE, NULL, &cmd_map);    /* ^P */
  69.     add_bind("W", C_WRITE_LIST, NULL, &cmd_map);
  70.     add_bind("w", C_WRITE_MSG, NULL, &cmd_map);
  71.     add_bind("U", C_UNDEL_LIST, NULL, &cmd_map);
  72.     add_bind("u", C_UNDEL_MSG, NULL, &cmd_map);
  73.     add_bind("S", C_SAVE_LIST, NULL, &cmd_map);
  74.     add_bind("s", C_SAVE_MSG, NULL, &cmd_map);
  75.     add_bind("f", C_FOLDER, NULL, &cmd_map);
  76.     add_bind("D", C_DELETE_LIST, NULL, &cmd_map);
  77.     add_bind("d", C_DELETE_MSG, NULL, &cmd_map);
  78.     add_bind("C", C_COPY_LIST, NULL, &cmd_map);
  79.     add_bind("c", C_COPY_MSG, NULL, &cmd_map);
  80.  
  81.     /* Cursor movement and message selection */
  82.     add_bind("g", C_GOTO_MSG, NULL, &cmd_map);
  83.     add_bind("}", C_BOTTOM_PAGE, NULL, &cmd_map);
  84.     add_bind("{", C_TOP_PAGE, NULL, &cmd_map);
  85.     add_bind("$", C_LAST_MSG, NULL, &cmd_map);
  86.     add_bind("^", C_FIRST_MSG, NULL, &cmd_map);
  87.     add_bind("\013",C_PREV_MSG, NULL, &cmd_map);    /* ^K */
  88.     add_bind("\012", C_NEXT_MSG, NULL, &cmd_map);    /* ^J */
  89.     add_bind("-",C_PREV_MSG, NULL, &cmd_map);
  90.     add_bind("+",C_NEXT_MSG, NULL, &cmd_map);
  91.     add_bind("K", C_PREV_MSG, NULL, &cmd_map);
  92.     add_bind("k", C_PREV_MSG, NULL, &cmd_map);
  93.     add_bind("J", C_NEXT_MSG, NULL, &cmd_map);
  94.     add_bind("j", C_NEXT_MSG, NULL, &cmd_map);
  95.  
  96.     /* Mail-sending commands */
  97.     add_bind("R", C_REPLY_ALL, NULL, &cmd_map);
  98.     add_bind("r", C_REPLY_SENDER, NULL, &cmd_map);
  99.     add_bind("M", C_MAIL_FLAGS, NULL, &cmd_map);
  100.     add_bind("m", C_MAIL, NULL, &cmd_map);
  101.  
  102.     /* Mail-reading commands */
  103.     add_bind(".", C_DISPLAY_MSG, NULL, &cmd_map);
  104.     add_bind("T", C_TOP_MSG, NULL, &cmd_map);
  105.     add_bind("t", C_DISPLAY_MSG, NULL, &cmd_map);
  106.     add_bind("p", C_DISPLAY_MSG, NULL, &cmd_map);
  107.     add_bind("n", C_DISPLAY_NEXT, NULL, &cmd_map);
  108.  
  109. #endif /* CURSES */
  110. }
  111.  
  112. /* Bindable function names.
  113.  *  Most of these can't be used if CURSES is not defined,
  114.  *  but help and lookups get confused if they aren't all here.
  115.  */
  116. struct cmd_map map_func_names[] = {
  117.     /* These MUST be in numerical order; see bindings.h */
  118.     { C_NULL,        "no-op",        NULL, NULL_MAP },
  119.     { C_GOTO_MSG,    "goto-msg",        NULL, NULL_MAP },
  120.     { C_WRITE_LIST,    "write-list",        NULL, NULL_MAP },
  121.     { C_WRITE_MSG,    "write",        NULL, NULL_MAP },
  122.     { C_SAVE_LIST,    "save-list",        NULL, NULL_MAP },
  123.     { C_SAVE_MSG,    "save",            NULL, NULL_MAP },
  124.     { C_COPY_LIST,    "copy-list",        NULL, NULL_MAP },
  125.     { C_COPY_MSG,    "copy",            NULL, NULL_MAP },
  126.     { C_DELETE_LIST,    "delete-list",        NULL, NULL_MAP },
  127.     { C_DELETE_MSG,    "delete",        NULL, NULL_MAP },
  128.     { C_UNDEL_LIST,    "undelete-list",    NULL, NULL_MAP },
  129.     { C_UNDEL_MSG,    "undelete",        NULL, NULL_MAP },
  130.     { C_REDRAW,        "redraw",        NULL, NULL_MAP },
  131.     { C_REVERSE,    "reverse-video",    NULL, NULL_MAP },
  132.     { C_NEXT_MSG,    "next-msg",        NULL, NULL_MAP },
  133.     { C_PREV_MSG,    "back-msg",        NULL, NULL_MAP },
  134.     { C_FIRST_MSG,    "first-msg",        NULL, NULL_MAP },
  135.     { C_LAST_MSG,    "last-msg",        NULL, NULL_MAP },
  136.     { C_TOP_PAGE,    "top-page",        NULL, NULL_MAP },
  137.     { C_BOTTOM_PAGE,    "bottom-page",        NULL, NULL_MAP },
  138.     { C_NEXT_SCREEN,    "screen-next",        NULL, NULL_MAP },
  139.     { C_PREV_SCREEN,    "screen-back",        NULL, NULL_MAP },
  140.     { C_SOURCE,        "source",        NULL, NULL_MAP },
  141.     { C_SAVEOPTS,    "saveopts",        NULL, NULL_MAP },
  142.     { C_NEXT_SEARCH,    "search-next",        NULL, NULL_MAP },
  143.     { C_PREV_SEARCH,    "search-back",        NULL, NULL_MAP },
  144.     { C_CONT_SEARCH,    "search-again",        NULL, NULL_MAP },
  145.     { C_PRESERVE,    "preserve",        NULL, NULL_MAP },
  146.     { C_REV_SORT,    "sort-reverse",        NULL, NULL_MAP },
  147.     { C_SORT,        "sort",            NULL, NULL_MAP },
  148.     { C_QUIT_HARD,    "quit!",        NULL, NULL_MAP },
  149.     { C_QUIT,        "quit",            NULL, NULL_MAP },
  150.     { C_EXIT_HARD,    "exit!",        NULL, NULL_MAP },
  151.     { C_EXIT,        "exit",            NULL, NULL_MAP },
  152.     { C_UPDATE,        "update",        NULL, NULL_MAP },
  153.     { C_FOLDER,        "folder",        NULL, NULL_MAP },
  154.     { C_SHELL_ESC,    "shell-escape",        NULL, NULL_MAP },
  155.     { C_CURSES_ESC,    "line-mode",        NULL, NULL_MAP },
  156.     { C_PRINT_MSG,    "lpr",            NULL, NULL_MAP },
  157.     { C_CHDIR,        "chdir",        NULL, NULL_MAP },
  158.     { C_VAR_SET,    "variable",        NULL, NULL_MAP },
  159.     { C_IGNORE,        "ignore",        NULL, NULL_MAP },
  160.     { C_ALIAS,        "alias",        NULL, NULL_MAP },
  161.     { C_OWN_HDR,    "my-hdrs",        NULL, NULL_MAP },
  162.     { C_VERSION,    "version",        NULL, NULL_MAP },
  163.     { C_MAIL_FLAGS,    "mail-flags",        NULL, NULL_MAP },
  164.     { C_MAIL,        "mail",            NULL, NULL_MAP },
  165.     { C_REPLY_ALL,    "reply-all",        NULL, NULL_MAP },
  166.     { C_REPLY_SENDER,    "reply",        NULL, NULL_MAP },
  167.     { C_DISPLAY_NEXT,    "display-next",        NULL, NULL_MAP },
  168.     { C_DISPLAY_MSG,    "display",        NULL, NULL_MAP },
  169.     { C_TOP_MSG,    "top",            NULL, NULL_MAP },
  170.     { C_BIND_MACRO,    "bind-macro",        NULL, NULL_MAP },
  171.     { C_BIND,        "bind",            NULL, NULL_MAP },
  172.     { C_UNBIND,        "unbind",        NULL, NULL_MAP },
  173.     { C_MAP_BANG,    "map!",            NULL, NULL_MAP },
  174.     { C_MAP,        "map",            NULL, NULL_MAP },
  175.     { C_MACRO,        "macro",        NULL, NULL_MAP },
  176.     /* C_HELP Must be the last one! */
  177.     { C_HELP,        "help",            NULL, NULL_MAP }
  178. };
  179.  
  180. #ifdef CURSES
  181.  
  182. /*
  183.  * getcmd() is called from curses mode only.  It waits for char input from
  184.  * the user via m_getchar() (which means that a macro could provide input)
  185.  * and then compares the chars input against the "bind"ings set up by the
  186.  * user (or the defaults).  For example, 'j' could bind to "next msg" which
  187.  * is interpreted by the big switch statement in curses_command() (curses.c).
  188.  * getcmd() returns the int-value of the curses command the input is "bound"
  189.  * to.  If the input is unrecognized, C_NULL is returned (curses_command()
  190.  * might require some cleanup, so this is valid, too).
  191.  *
  192.  * Since the input could originate from a macro rather than the terminal,
  193.  * check to see if this is the case and search for a '[' char which indicates
  194.  * that there is a curses command or other "long" command to be executed.
  195.  */
  196. getcmd()
  197. {
  198.     char         buf[MAX_BIND_LEN * 3];
  199.     register int     c, m, match;
  200.     register char    *p = buf;
  201.     register struct cmd_map *list;
  202.  
  203.     bzero(buf, MAX_BIND_LEN);
  204.     active_cmd = NULL_MAP;
  205.     c = m_getchar();
  206.     /* If user did job control (^Z), then the interrupt flag will be
  207.      * set.  Be sure it's unset before continuing.
  208.      */
  209.     turnoff(glob_flags, WAS_INTR);
  210.     if (isdigit(c)) {
  211.     buf[0] = c;
  212.     buf[1] = '\0';
  213.     Ungetstr(buf); /* So mac_flush can clear on error */
  214.     return C_GOTO_MSG;
  215.     }
  216.     for (;;) {
  217.     if (ison(glob_flags, IN_MACRO) && c == MAC_LONG_CMD)
  218.         return long_mac_cmd(c, TRUE);
  219.     else
  220.         *p++ = c;
  221.     m = 0;
  222.     for (list = cmd_map; list; list = list->m_next) {
  223.         if ((match = prefix(buf, list->m_str)) == MATCH) {
  224.         if (debug)
  225.             print("\"%s\" ",
  226.             ctrl_strcpy(buf,
  227.                     map_func_names[list->m_cmd].m_str,
  228.                     TRUE));
  229.         if (list->m_cmd == C_MACRO) {
  230.             curs_macro(list->x_str);
  231.             return getcmd();
  232.         }
  233.         active_cmd = list;
  234.         return (int)list->m_cmd;
  235.         } else if (match != NO_MATCH)
  236.         m++;
  237.     }
  238.     if (m == 0) {
  239.         if (debug) {
  240.         char tmp[sizeof buf];
  241.         print("No binding for \"%s\" found.",
  242.             ctrl_strcpy(tmp, buf, TRUE));
  243.         }
  244.         return C_NULL;
  245.     }
  246.     c = m_getchar();
  247.     }
  248. }
  249.  
  250. #endif /* CURSES */
  251.  
  252. /*
  253.  * bind_it() is used to set or unset bind, map and map! settings.
  254.  * bind is used to accelerate curses commands by mapping key sequences
  255.  * to curses commands.  map is used to accelerate command mode keysequences
  256.  * by simulating stdin.  map! is the same, but used when in compose mode.
  257.  *
  258.  * bind_it() doesn't touch messages; return -1 for curses mode.
  259.  * return -2 to have curses command set CNTD_CMD to prevent screen refresh
  260.  * to allow user to read output in case of multiple lines.
  261.  *
  262.  * Since this routine deals with a lot of binding and unbinding of things
  263.  * like line-mode "map"s and is interactive (calls Getstr()), be very careful
  264.  * not to allow expansions during interaction.
  265.  */
  266. bind_it(len, argv)
  267. char **argv;
  268. {
  269.     char string[MAX_BIND_LEN], buf[256], *name = NULL;
  270.     char *rawstr; /* raw format of string (ptr to string if no argv avail) */
  271.     char ascii[MAX_BIND_LEN*2]; /* printable ascii version of string */
  272.     register int x;
  273.     SIGRET (*oldint)(), (*oldquit)();
  274.     struct cmd_map **map_list;
  275.     int unbind = (argv && **argv == 'u');
  276.     int map = 0, is_bind_macro = 0;
  277.     int ret = 0 - iscurses; /* return value */
  278.  
  279.     if (argv && !strcmp(name = *argv, "bind-macro"))
  280.     is_bind_macro++;
  281.  
  282.     if (map = (argv && (!strcmp(name, "map!") || !strcmp(name, "unmap!"))))
  283.     map_list = &bang_map;
  284.     else if (map = (argv && (!strcmp(name, "map") || !strcmp(name, "unmap"))))
  285.     map_list = &line_map;
  286.     else
  287.     map_list = &cmd_map;
  288.  
  289.     if (argv && *++argv && !strcmp(*argv, "-?"))
  290.     /* Subtract ret and iscurses to signal output */
  291.     return help(0, unbind? name+2 : name, cmd_help) - ret - iscurses;
  292.  
  293.     if (iscurses)
  294.     on_intr();
  295.  
  296.     if (unbind) {
  297.     if (!*argv) {
  298.         char savec = complete;
  299.         complete = 0;
  300.         print("%s what? ", name);
  301.         len = Getstr(buf, sizeof buf, 0);
  302.         complete = savec;
  303.         if (len <= 0) {
  304.         if (iscurses)
  305.             off_intr();
  306.         return -1;
  307.         }
  308.         rawstr = m_xlate(buf);
  309.     } else
  310.         rawstr = m_xlate(*argv);
  311.     if (!un_bind(rawstr, map_list)) {
  312.         (void) ctrl_strcpy(ascii, rawstr, TRUE);
  313.         print("\"%s\" isn't bound to a command.\n", ascii);
  314.     }
  315.     if (iscurses)
  316.         off_intr();
  317.     return ret;
  318.     }
  319.     if (argv && *argv) {
  320.     rawstr = m_xlate(*argv);
  321.     (void) ctrl_strcpy(ascii, rawstr, TRUE);
  322.     if (!*++argv) {
  323.         /*
  324.          * determine whether "argv" references a "map" or a "bind"
  325.          */
  326.         int binding = c_bind(rawstr, *map_list);
  327.         if (binding == C_MACRO) {
  328.         char *mapping = c_macro(NULL, rawstr, *map_list);
  329.         if (mapping) {
  330.             print("\"%s\" is mapped to ", ascii);
  331.             print_more("\"%s\".\n",
  332.             ctrl_strcpy(buf, mapping, FALSE));
  333.         } else
  334.             print("\"%s\" isn't mapped.\n", ascii);
  335.         } else if (binding)
  336.         print("\"%s\" is %s to \"%s\".\n", ascii,
  337.             map? "mapped" : "bound", map_func_names[binding].m_str);
  338.         else if (map)
  339.         print("\"%s\" isn't mapped.\n", ascii);
  340.         else
  341.         print("\"%s\" isn't bound to a command.\n", ascii);
  342.         if (iscurses)
  343.         off_intr();
  344.         return ret;
  345.     }
  346.     } else {
  347.     char savec = complete;
  348.     complete = 0;
  349.     print("%s [<CR>=all, -?=help]: ", name);
  350.     len = Getstr(string, MAX_BIND_LEN-1, 0);
  351.     complete = savec;
  352.     if (len == 0) {
  353.         int add_to_ret = iscurses;
  354. #ifdef CURSES
  355.         if (iscurses)
  356.         move(LINES-1, 0), refresh();
  357. #endif
  358.         if (map || is_bind_macro)
  359.         add_to_ret = !c_macro(name, NULL, *map_list);
  360.         else
  361.         add_to_ret = !c_bind(NULL, *map_list);
  362.         if (iscurses)
  363.         off_intr();
  364.         /* signal CTND_CMD if there was output */
  365.         return ret - add_to_ret;
  366.     }
  367.     if (len < 0) {
  368.         if (iscurses)
  369.         off_intr();
  370.         return ret;
  371.     }
  372.     rawstr = m_xlate(string);
  373.     (void) ctrl_strcpy(ascii, rawstr, TRUE);
  374.     }
  375.     /* if a binding was given on the command line */
  376.     if (argv && *argv && !map)
  377.     if (is_bind_macro)
  378.         (void) strcpy(buf, "macro");
  379.     else
  380.         (void) strcpy(buf, *argv++);
  381.     else {
  382.     /* at this point, "rawstr" and "ascii" should both be set */
  383.     int binding;
  384.  
  385.     if (!strcmp(ascii, "-?")) {
  386.         if (iscurses)
  387.         clr_bot_line();
  388.         ret -= help(0, name, cmd_help);
  389.         if (iscurses)
  390.         off_intr();
  391.         /* Subtract iscurses to signal CNTD_CMD */
  392.         return ret - iscurses;
  393.     }
  394.  
  395.     if (!map && !is_bind_macro) {
  396.         binding = c_bind(rawstr, *map_list);
  397.  
  398.         for (len = 0; len == 0; ) {
  399.         print("\"%s\" = <%s>: New binding [<CR> for list]: ",
  400.             ascii, (binding? map_func_names[binding].m_str : "unset"));
  401.         len = Getstr(buf, sizeof buf, 0);
  402.         if (iscurses)
  403.             clr_bot_line();
  404.         /* strip any trailing whitespace */
  405.         if (len > 0)
  406.             len = no_newln(buf) - buf;
  407.         if (len == 0) {
  408.             (void) do_pager(NULL, TRUE);
  409.             if (iscurses)
  410.             putchar('\n');
  411.             for (x = 1; x <= C_HELP; x++) {
  412.             if (!(x % 4))
  413.                 if (do_pager("\n", FALSE) == EOF)
  414.                 break;
  415.             (void) do_pager(sprintf(buf, "%-15.15s  ",
  416.                         map_func_names[x].m_str), FALSE);
  417.             }
  418.             (void) do_pager("\n", FALSE);
  419.             (void) do_pager(NULL, FALSE);
  420.             ret -= iscurses;
  421.         }
  422.         }
  423.     } else /* map */
  424.         (void) strcpy(buf, "macro"), len = 5;
  425.     /* if list was printed, ret < -1 -- tells CNTD_CMD to be set and
  426.      * prevents screen from being refreshed (lets user read output
  427.      */
  428.     if (len == -1) {
  429.         if (iscurses)
  430.         off_intr();
  431.         return ret;
  432.     }
  433.     }
  434.     for (x = 1; x <= C_HELP; x++) {
  435.     if (prefix(buf, map_func_names[x].m_str) == MATCH) {
  436.         int add_to_ret;
  437.         if (debug)
  438.         print("\"%s\" will execute \"%s\".\n", ascii, buf);
  439.         if (map_func_names[x].m_cmd == C_MACRO) {
  440.         if (argv && *argv) {
  441.             (void) argv_to_string(buf, argv);
  442.             (void) m_xlate(buf); /* Convert buf to raw chars */
  443.             add_to_ret =
  444.             do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
  445.         } else {
  446.             char exp[MAX_MACRO_LEN*2]; /* printable expansion */
  447.             char *mapping = c_macro(NULL, rawstr, *map_list);
  448.  
  449.             if (mapping)
  450.             (void) ctrl_strcpy(exp, mapping, TRUE);
  451.             print("\"%s\" = <%s>", ascii, mapping ? exp : "unset");
  452.             putchar('\n'), print("New macro: ");
  453.             ret -= iscurses; /* To signal screen messed up */
  454.             /* we are done with buf, so we can trash over it */
  455.             len = Getstr(buf, MAX_MACRO_LEN, 0);
  456.             if (len > 0) {
  457.             if (iscurses)
  458.                 clr_bot_line();
  459.             (void) m_xlate(buf); /* Convert buf to raw chars */
  460.             add_to_ret =
  461.                 do_bind(rawstr, C_MACRO, buf, map_list);
  462.             if (debug) {
  463.                 (void) ctrl_strcpy(exp, buf, TRUE);
  464.                 print("\"%s\" will execute \"%s\".\n", ascii, exp);
  465.             }
  466.             } else if (len < 0) {
  467.             if (iscurses)
  468.                 off_intr();
  469.             return ret;
  470.             } else
  471.             print("Can't bind to null macro"), putchar('\n');
  472.         }
  473.         } else /* not a macro */ {
  474.         (void) argv_to_string(buf, argv);
  475.         add_to_ret =
  476.             do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
  477.         }
  478.         /* if do_bind had no errors, it returned -1.  If we already
  479.          * messed up the screen, then ret is less than -1.  return the
  480.          * lesser of the two to make sure that CNTD_CMD gets set right
  481.          */
  482.         if (iscurses)
  483.         off_intr();
  484.         return min(add_to_ret, ret);
  485.     }
  486.     }
  487.     print("\"%s\": Unknown function.\n", buf);
  488.     if (iscurses)
  489.     off_intr();
  490.     return ret;
  491. }
  492.  
  493. /*
  494.  * print current key to command bindings if "str" is NULL.
  495.  * else return the integer "m_cmd" which the str is bound to.
  496.  */
  497. c_bind(str, opts)
  498. register char *str;
  499. register struct cmd_map *opts;
  500. {
  501.     register int    incurses = iscurses;
  502.  
  503.     if (!str) {
  504.     if (!opts) {
  505.         print("No command bindings.\n");
  506.         return C_ERROR;
  507.     }
  508.     if (incurses)
  509.         clr_bot_line(), iscurses = FALSE;
  510.     (void) do_pager(NULL, TRUE);
  511.     (void) do_pager("Current key to command bindings:\n", FALSE);
  512.     (void) do_pager("\n", FALSE);
  513.     }
  514.  
  515.     for (; opts; opts = opts->m_next) {
  516.     char buf[BUFSIZ], buf2[MAX_BIND_LEN], exp[MAX_MACRO_LEN*2], *xp;
  517.     if (!str) {
  518.         (void) ctrl_strcpy(buf2, opts->m_str, FALSE);
  519.         if ((xp = opts->x_str) && opts->m_cmd == C_MACRO)
  520.         xp = ctrl_strcpy(exp, opts->x_str, TRUE);
  521.         if (do_pager(sprintf(buf, "%s\t%-15.15s %s\n",
  522.              buf2, map_func_names[opts->m_cmd].m_str,
  523.              xp? xp : ""),
  524.              FALSE) == EOF)
  525.         break;
  526.     } else
  527.         if (strcmp(str, opts->m_str))
  528.         continue;
  529.         else
  530.         return opts->m_cmd;
  531.     }
  532.  
  533.     iscurses = incurses;
  534.     if (!str)
  535.     (void) do_pager(NULL, FALSE);
  536.     return C_NULL;
  537. }
  538.  
  539. /*
  540.  * Doesn't touch messages, but changes macros: return -1.
  541.  * Error output causes return < -1.
  542.  *  args is currently the execute string of a macro mapping, but may be
  543.  *  used in the future as an argument string for any curses command.
  544.  */
  545. do_bind(str, func, args, map_list)
  546. register char *str, *args;
  547. struct cmd_map **map_list;
  548. long func;
  549. {
  550.     register int ret = -1;
  551.     register struct cmd_map *list;
  552.     int match;
  553.  
  554.     if (func == C_MACRO && !check_mac_bindings(args))
  555.     --ret;
  556.     (void) un_bind(str, map_list);
  557.     for (list = *map_list; list; list = list->m_next)
  558.     if ((match = prefix(str, list->m_str)) != NO_MATCH) {
  559.         ret--;
  560.         switch (match) {
  561.         case MATCH:
  562.             puts("Something impossible just happened.");
  563.         when A_PREFIX_B:
  564.             wprint("Warning: \"%s\" prefixes \"%s\" (%s)\n", str,
  565.             list->m_str, map_func_names[list->m_cmd].m_str);
  566.         when B_PREFIX_A:
  567.             wprint("Warning: \"%s\" (%s) prefixes: \"%s\"\n",
  568.             list->m_str, map_func_names[list->m_cmd].m_str, str);
  569.         }
  570.     }
  571.     add_bind(str, func, args, map_list);
  572.     /* errors decrement ret.  If ret returns less than -1, CNTD_CMD is set
  573.      * and no redrawing is done so user can see the warning signs
  574.      */
  575.     return ret;
  576. }
  577.  
  578. /*
  579.  * add a binding to a list.  This may include "map"s or other mappings since
  580.  * the map_list argument can control that.  The "func" is an int defined in
  581.  * bindings.h ... the "str" passed is the string the user would have to type
  582.  * to get the macro/map/binding expanded.  This must in in raw format: no
  583.  * \n's to mean \015.  Convert first using m_xlate().
  584.  */
  585. add_bind(str, func, args, map_list)
  586. register char *str, *args;
  587. struct cmd_map **map_list;
  588. long func;
  589. {
  590.     register struct cmd_map *tmp;
  591.  
  592.     if (!str || !*str)
  593.     return;
  594.  
  595.     /* now make a new option struct and set fields */
  596.     if (!(tmp = (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) {
  597.     error("calloc");
  598.     return;
  599.     }
  600.     tmp->m_next = *map_list;
  601.     *map_list = tmp;
  602.  
  603.     tmp->m_str = savestr(str);
  604.     tmp->m_cmd = func; /* strdup handles the NULL case */
  605.     if (args && *args)
  606.     tmp->x_str = savestr(args);
  607.     else
  608.     tmp->x_str = NULL;
  609. }
  610.  
  611. static
  612. un_bind(p, map_list)
  613. register char *p;
  614. struct cmd_map **map_list;
  615. {
  616.     register struct cmd_map *list = *map_list, *tmp;
  617.  
  618.     if (!list || !*list->m_str || !p || !*p)
  619.     return 0;
  620.  
  621.     if (!strcmp(p, (*map_list)->m_str)) {
  622.     *map_list = (*map_list)->m_next;
  623.     xfree (list->m_str);
  624.     if (list->x_str)
  625.         xfree (list->x_str);
  626.     xfree((char *)list);
  627.     return 1;
  628.     }
  629.     for ( ; list->m_next; list = list->m_next)
  630.     if (!strcmp(p, list->m_next->m_str)) {
  631.         tmp = list->m_next;
  632.         list->m_next = list->m_next->m_next;
  633.         xfree (tmp->m_str);
  634.         if (tmp->x_str)
  635.         xfree (tmp->x_str);
  636.         xfree ((char *)tmp);
  637.         return 1;
  638.     }
  639.     return 0;
  640. }
  641.  
  642. prefix(a, b)
  643. register char *a, *b;
  644. {
  645.     if (!a || !b)
  646.     return NO_MATCH;
  647.  
  648.     while (*a && *b && *a == *b)
  649.     a++, b++;
  650.     if (!*a && !*b)
  651.     return MATCH;
  652.     if (!*a && *b)
  653.     return A_PREFIX_B;
  654.     if (*a && !*b)
  655.     return B_PREFIX_A;
  656.     return NO_MATCH;
  657. }
  658.